import java.awt.AWTException;
import java.awt.Color;
import java.awt.HeadlessException;

import java.awt.Graphics;
import javax.swing.*;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.*;
import java.awt.event.*;
import java.io.*;
import java.awt.*;
import javax.imageio.ImageIO;

import java.util.*;

import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

public class TinCDCanvas extends JPanel implements MouseListener, MouseMotionListener{
		
	/*int block*/
	int x = 0;
	int y = 0;
	int oldX,oldY;
	int panelHeight = 0;
	int panelWidth = 0;
	int newInstance = 0;
	int alkaneCounter = 0;
	protected int bufferCounter = 0;
	
	/*float block*/
	float newTheta;
	
	/*double block*/
	double theta, newX, newY;
	final double PI = 3.14159265358979323846;
	
	/*boolean block*/
	protected boolean drawTextBox = false;
	protected boolean drawTrans = true;
	protected boolean drawBoundingBox = true;
	boolean wordWrap = false;
	boolean drawSavedItem = false;
	protected boolean drawText = false;
	protected boolean drawLine = true;
	protected boolean drawChain = false;
	protected boolean drawBenzene = false;
	protected boolean drawCyclohexane = false;
	protected boolean drawWedge = false;
	protected boolean drawDash = false;
	protected boolean drawDouble = false;
	protected boolean drawTriple = false;
	protected boolean drawIndivAtom = false;
	private boolean drawNewLine = false;

	/*Array block*/
	protected char test[] = new char[1024];
	protected Line2D.Double alkane[] = new Line2D.Double[100];
	protected AtomicBond atomArray[] = new AtomicBond[100];
	
	/*Colors*/
	private Color backgroundColor = Color.BLACK;
	private Color lineColor = Color.WHITE;
	private Color transColor = new Color(1.0f,1.0f,1.0f,0.5f);
	
	/*Individual Atom String*/
	String indivAtom = "";
	
	/*NULL declaration*/
	Line2D.Double NULL = new Line2D.Double(0,0,0,0);
	
	/*MainFrame Instance*/
	private MainFrame TinCDCopy;
	
	/*Main graphics object*/
	private Graphics2D gg;
	
	/*Mouse Object*/
	Point cursorLocation;
	
	TinCDCanvas(MainFrame TinCD)
	{
		TinCDCopy = TinCD;
		addMouseMotionListener(this);
		addMouseListener(this);
	}
	
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		gg = (Graphics2D)g;
		setupCanvas();
		
		/*drawALine(gg);
		if(drawSavedItem == true)
		{
			for(int i = 0; i < alkaneCounter; i++)
			{
				gg.draw(alkane[i]);
			}
		}*/
		if (drawLine == true)
		{
			drawALine(gg);
		}
		else if(drawTextBox == true)
		{
			drawATextBox(gg);
		}
		
		else if(drawBenzene == true)
		{
			drawBenzene(gg);
		}
		
		else if(drawCyclohexane == true)
		{
			drawCyclohexane(gg);
		}
		else if(drawChain == true)
		{
			drawChain(gg);
		}
		else if(drawWedge == true)
		{
			drawWedgeBond(gg);
		}
		else if(drawDash == true)
		{
			drawDashBond(gg);
		}
		else if(drawDouble == true)
		{
			drawDoubleBond(gg);
		}
		else if(drawTriple == true)
		{
			drawTripleBond(gg);
		}
		else if (drawIndivAtom == true)
		{
			drawIndivAtom(gg);
		}
	}

/*initialization and setters*/
	
	public void setupCanvas()
	{
		panelHeight = TinCDCopy.getHeight();
		panelWidth = TinCDCopy.getWidth();
			
		gg.setColor(backgroundColor);
		gg.fillRect(0, 0, panelWidth, panelHeight);
		
		gg.setPaint(lineColor);
		gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		gg.setStroke(new BasicStroke(1.4f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
	}

	public void setBackgroundColor(Color color)
	{
		backgroundColor = color;
		repaint();
	}

	public void setLineColor(Color color)
	{
		lineColor = color;
		repaint();
	}
 
/*end initialization and setters*/

/*drawing logic*/	
	public void drawATextBox(Graphics2D graphics)
	{
		int upperLeftX = 0;
		int upperLeftY = 0;
		graphics.setPaint(new Color(1.0f,0.0f,0.0f,1.0f));
		
		if(drawBoundingBox == true)
		{
			graphics.draw(new Line2D.Double(oldX,oldY,x,oldY)); //Top bounding line
			graphics.draw(new Line2D.Double(oldX,oldY,oldX,y)); //Left bounding line
			graphics.draw(new Line2D.Double(oldX,y,x,y)); //bottom bounding line
			graphics.draw(new Line2D.Double(x,oldY,x,y)); // right bounding line
		}
		
		if (drawTrans == true)
		{
			graphics.setPaint(new Color(1.0f,0.0f,0.0f,0.2f));
		
			//semi-transparent box
			//drawing logic
			if(x > oldX && y > oldY)
				{
					graphics.fillRect(oldX, oldY, Math.abs(oldX-x), Math.abs(oldY-y));
					upperLeftX = oldX;
					upperLeftY = oldY;
				}
			else if (x < oldX && y > oldY)
			{
				graphics.fillRect(x, oldY, Math.abs(oldX-x), Math.abs(oldY-y));
				upperLeftX = x;
				upperLeftY = oldY;
			}
			else if ((x < oldX && y < oldY))
			{
				graphics.fillRect(x, y, Math.abs(oldX-x), Math.abs(oldY-y));
				upperLeftX = x;
				upperLeftY = y;
			}
			else
			{
				graphics.fillRect(oldX, y, Math.abs(oldX-x), Math.abs(oldY-y));
				upperLeftX = oldX;
				upperLeftY = y;
			}
		
		}
		
		Font defaultFont = graphics.getFont();
		graphics.setPaint(lineColor);
	
		graphics.drawChars(test, 0, bufferCounter, upperLeftX, upperLeftY+defaultFont.getSize());

		drawTextBox = true;	
		
		repaint();

	}
	
	public void drawALine(Graphics2D graphics)
	{
		
			graphics.draw(new Line2D.Double(oldX,oldY,newX,newY));
		
		repaint();
	}
	
	public void drawChain(Graphics2D graphics)
	{	
		int numOfChain = (int)Math.sqrt(Math.pow(x-oldX, 2)+Math.pow(y-oldY,2))/30;
		
		int nextX = oldX;
		int nextY = oldY;
		
		graphics.rotate(newTheta, oldX, oldY);
		for (int i = 0; i < numOfChain; i++)
		{
			if(i%2 == 0)
			{
				graphics.draw(new Line2D.Double(nextX,nextY,nextX+30,nextY-30));
				nextX = nextX+30;
				nextY = nextY-30;
			}
			else
			{
				graphics.draw(new Line2D.Double(nextX,nextY,nextX+30,nextY+30));
				nextX = nextX+30;
				nextY = nextY+30;
			}			
		}
		repaint();
	}
	
	public void drawBenzene(Graphics2D graphics)
	{
		//refactor with trig functions
		graphics.rotate(theta, oldX, oldY);
		
		graphics.draw(new Line2D.Double(oldX,oldY,oldX+35,oldY-20));
		graphics.draw(new Line2D.Double(oldX,oldY,oldX+35,oldY-20));
		graphics.draw(new Line2D.Double(oldX+70,oldY,oldX+35,oldY-20));
		graphics.draw(new Line2D.Double(oldX,oldY,oldX,oldY+40));
		graphics.draw(new Line2D.Double(oldX+70,oldY,oldX+70,oldY+40));
		graphics.draw(new Line2D.Double(oldX,oldY+40,oldX+35,oldY+60));
		graphics.draw(new Line2D.Double(oldX+70,oldY+40,oldX+35,oldY+60));
		//Double Bond lines
		graphics.draw(new Line2D.Double(oldX+35,oldY-15,oldX+65,oldY+2));
		graphics.draw(new Line2D.Double(oldX+5,oldY+2.5f,oldX+5,oldY+37.5f));
		graphics.draw(new Line2D.Double(oldX+35,oldY+55,oldX+65,oldY+38));
		
		repaint();
	}
	
	public void drawCyclohexane(Graphics2D graphics)
	{
		graphics.rotate(theta,oldX,oldY);
		graphics.draw(new Line2D.Double(oldX,oldY,oldX+35,oldY-20));
		
		graphics.draw(new Line2D.Double(oldX+70,oldY,oldX+35,oldY-20));
		graphics.draw(new Line2D.Double(oldX,oldY,oldX,oldY+40));
		graphics.draw(new Line2D.Double(oldX+70,oldY,oldX+70,oldY+40));
		graphics.draw(new Line2D.Double(oldX,oldY+40,oldX+35,oldY+60));
		graphics.draw(new Line2D.Double(oldX+70,oldY+40,oldX+35,oldY+60));
	
		repaint();
	}
	
	public void drawDoubleBond(Graphics2D graphics)
	{	
		double theta;
		double result;
		double newBondX, newBondY;
		double newAnchorX;
		double newAnchorY;
		
		theta = Math.atan2(y-oldY, x-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newBondX = (42*(Math.cos((theta-PI/20)))+oldX);
		newBondY = (42*(Math.sin((theta-PI/20)))+oldY);
		
		theta = Math.atan2(newBondY-oldY,newBondX-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newAnchorX = ((-42*(Math.cos((theta)))+ newBondX));
		newAnchorY = ((-42*(Math.sin((theta)))+ newBondY));
		
		graphics.draw(new Line2D.Double(oldX,oldY,newX,newY));
		graphics.draw(new Line2D.Double(newBondX,newBondY,newAnchorX,newAnchorY));
		repaint();
	}

	public void drawTripleBond(Graphics2D graphics)
	{
		double theta;
		double result;
		double newBondX, newBondY;
		double newAnchorX;
		double newAnchorY;

		graphics.draw(new Line2D.Double(oldX,oldY,newX,newY));
		
		theta = Math.atan2(y-oldY, x-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newBondX = (42*(Math.cos((theta-PI/20)))+oldX);
		newBondY = (42*(Math.sin((theta-PI/20)))+oldY);
		
		theta = Math.atan2(newBondY-oldY,newBondX-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newAnchorX = ((-42*(Math.cos((theta)))+ newBondX));
		newAnchorY = ((-42*(Math.sin((theta)))+ newBondY));
		
		graphics.draw(new Line2D.Double(newBondX,newBondY,newAnchorX,newAnchorY));
		
		theta = Math.atan2(y-oldY, x-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newBondX = (42*(Math.cos((theta+PI/20)))+oldX);
		newBondY = (42*(Math.sin((theta+PI/20)))+oldY);
		
		theta = Math.atan2(newBondY-oldY,newBondX-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newAnchorX = ((-42*(Math.cos((theta)))+ newBondX));
		newAnchorY = ((-42*(Math.sin((theta)))+ newBondY));
		
		graphics.draw(new Line2D.Double(newBondX,newBondY,newAnchorX,newAnchorY));
		
		
		repaint();
	}
	
	public void drawWedgeBond(Graphics2D graphics)
	{
		
		double theta;
		double result;
		int newBondX, newBondY;
		
		theta = Math.atan2(y-oldY, x-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newBondX = (int)(42*Math.cos((theta-PI/12))+oldX);
		newBondY = (int)(42*Math.sin((theta-PI/12))+oldY);
		
		//graphics.draw(new Line2D.Double(oldX,oldY,newX,newY));
		//graphics.draw(new Line2D.Double(oldX,oldY,newBondX,newBondY));
		//graphics.draw(new Line2D.Double(newX,newY,newBondX,newBondY));
		
		int xPoints[] = {oldX,(int)newX,newBondX};
		int yPoints[] = {oldY,(int)newY,newBondY};

		//graphics.drawPolygon(xPoints,yPoints,3);
		graphics.fillPolygon(xPoints, yPoints, 3);
		repaint();
	}
	
	public void drawDashBond(Graphics2D graphics)
	{
		double theta;
		double result;
		int newBondX, newBondY;
		
		theta = Math.atan2(y-oldY, x-oldX);
		result = Math.round(theta/(PI/6));
		theta = (float)(result*PI/6);
		newBondX = (int)(42*Math.cos((theta-PI/12))+oldX);
		newBondY = (int)(42*Math.sin((theta-PI/12))+oldY);
		
		//graphics.draw(new Line2D.Double(oldX,oldY,newX,newY));
		//graphics.draw(new Line2D.Double(oldX,oldY,newBondX,newBondY));
		//graphics.draw(new Line2D.Double(newX,newY,newBondX,newBondY));
		
		int xPoints[] = {oldX,(int)newX,newBondX};
		int yPoints[] = {oldY,(int)newY,newBondY};

		graphics.drawPolygon(xPoints,yPoints,3);
		graphics.setPaint(transColor);
		graphics.fillPolygon(xPoints, yPoints, 3);

		repaint();
	}
	
	public void drawIndivAtom(Graphics2D graphics)
	{
		graphics.drawString(indivAtom, x, y);
		repaint();
	}
	
	
	public void figureTheta(int testx, int testy)
	{
		double result;
		
		theta = Math.atan2(testy-oldY, testx-oldX);

		result = Math.round(theta/(PI/6));
	
		newTheta = (float) (result*(PI/6));

		newX = (double)((42*Math.cos(newTheta))+oldX);
		newY = (double)((42*Math.sin(newTheta))+oldY);
	}
	
/*end drawing logic*/
	
/*drawing saved items logic*/
public void addAlkane(int oldX, int oldY, double newX, double newY)
	{
		alkane[alkaneCounter++] = new Line2D.Double(oldX,oldY,newX,newY);
		atomArray[alkaneCounter] = new AtomicBond(new Carbon(), new Carbon());
	}

/*end drawing saved items logic*/

/*screenshot logic*/
public void testScreen(File file, String extension) throws IOException, AWTException
	{
		
		try {
			BufferedImage image = new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = image.createGraphics();
			super.paint(g);
			ImageIO.write(image, extension , new File(file.getAbsolutePath()+"."+extension));		
			}
			catch(Exception e) {}
			
	}
/*end screenshot logic*/

/*clear screen logic*/
	void clearJPanel()
	{
		drawLine = false;
		for(int i = 0; i <alkaneCounter; i++)
		{
			alkane[i] = null;
		}
		repaint();
	}
	
/*end clear screen logic*/
/*mouse handlers*/
	@Override
	public void mouseClicked(MouseEvent e) {
		
		//drawLine = true;
		
		/*if (drawTextBox == true)
		{
			oldX = (int)(e.getPoint().getX());
			oldY = (int)(e.getPoint().getY());
			x = (int)(e.getPoint().getX());
		    y = (int)(e.getPoint().getY());
		}*/
		
		oldX = (int)(e.getPoint().getX());
		oldY = (int)(e.getPoint().getY());
		x = (int)(e.getPoint().getX());
		y =(int)(e.getPoint().getY());
		newX = oldX-30;
		newY = oldY-30;
		figureTheta(x,y);

	}

	@Override
	public void mouseEntered(MouseEvent e) {
			
	}

	@Override
	public void mouseExited(MouseEvent e) {
		
	}

	@Override
	public void mousePressed(MouseEvent e) {

	}

	@Override
	public void mouseReleased(MouseEvent e) {
		
		//drawLine = false;

		//addAlkane(oldX,oldY,newX,newY);
		drawSavedItem = true;
	
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		
		x = (int)(e.getPoint().getX());
		y = (int)(e.getPoint().getY());
		figureTheta(x,y);
		
	}

	@Override
	public void mouseMoved(MouseEvent e) {

	}
	
/*end mouse handlers*/
	
}
